<?php

/**
 * Payment plan
 *
 * @author Andrés Barreto
 */
class Emms_PmtPlan {
    
    protected $_param;
    
    protected $_pmt;
    protected $_gracePmt;
        
    protected $_kp;
    protected $_kh;
    protected $_kl;
    protected $_ka;
        
    protected $_kpP;
    protected $_khP;
    protected $_klP;
    protected $_kaP;
        
    protected $_balanceKp;
    protected $_balanceKh;
    protected $_balanceKl;
    protected $_balanceKa;
    
    protected $_balancesToday;
        
//    private $_annualEffectiveRate;
                
    protected $_xpPmtDate;
    protected $_xpPmt;
    protected $_rFromDate;
    
    protected $_schPmt;
    protected $_schTotal;
    
    protected $_actualPaymentsNumber;
        
    protected $_metadata;
    protected $_schedule;
    
    public function __construct(Emms_Loan $loan = null) 
    {
        if($loan) {
            $this->_param = $loan->toArray();
            $this->_setMetadata();
            $this->_setPlan();  
            $this->_loadBalancesToday();
        }
    }
    
    protected function _setMetadata()
    {
        $translate = Zend_Registry::get('Zend_Translate');
        
        $this->_metadata['title'] = $this->translate("Payment Plan");
        $this->_metadata['headers'] = array
            (
                array('inx',               30, $translate->_("#"),              'Number'),
                array('Date',              80, $translate->_("Date"),           'DateTime', 'Date'),
                array('HealthInsurance',   80, $translate->_("Health ins."),    'Number',   'Standard'),
                array('LoanInsurance',     80, $translate->_("Loan ins."),      'Number',   'Standard'),
                array('AdministrativeFee', 80, $translate->_("Admin. fee"),     'Number',   'Standard'),
                array('Principal',         80, $translate->_("Principal"),      'Number',   'Standard'),
                array('Interest',          80, $translate->_("Interest"),       'Number',   'Standard'),
                array('Pmt',               80, $translate->_("Payment"),        'Number',   'Standard'),
                array('Penalties',         80, $translate->_("Penalties"),      'Number',   'Standard'),
                array('Delay',             60, $translate->_("Delay"),          'Number'),
                array('BalancePrincipal',  80, $translate->_("Principal bal."), 'Number',   'Standard'),
                array('ReceiptId',         80, $translate->_("Receipt"),        'Number'),
                array('TransactionId',     80, $translate->_("Transaction"),    'String'),
                array('Username',          80, $translate->_("Username"),       'String')
            );
    }
        
    protected function _setPlan()
    {  
        $this->_pmt      = $this->_param['Pmt'];
        $this->_gracePmt = $this->_param['GracePmt'];
        
        $this->_kp = $this->_param['Principal'];
        $this->_kh = 0.01 * $this->_param['Principal'] * $this->_param['HealthInsurancePct'];
        $this->_kl = 0.01 * $this->_param['Principal'] * $this->_param['LoanInsurancePct'];
        $this->_ka = 0.01 * $this->_param['Principal'] * $this->_param['AdministrativeFeePct'];
        
        $this->_kpP = 100 / (100 + $this->_param['HealthInsurancePct'] + $this->_param['LoanInsurancePct'] + $this->_param['AdministrativeFeePct']);
        $this->_khP = $this->_param['HealthInsurancePct'] / (100 + $this->_param['HealthInsurancePct'] + $this->_param['LoanInsurancePct'] + $this->_param['AdministrativeFeePct']);
        $this->_klP = $this->_param['LoanInsurancePct'] / (100 + $this->_param['HealthInsurancePct'] + $this->_param['LoanInsurancePct'] + $this->_param['AdministrativeFeePct']);
        $this->_kaP = $this->_param['AdministrativeFeePct'] / (100 + $this->_param['HealthInsurancePct'] + $this->_param['LoanInsurancePct'] + $this->_param['AdministrativeFeePct']);
        
        $this->_balanceKp = isset($this->_param['Loan']->d->BalancePrincipal)         ? $this->_param['Loan']->d->BalancePrincipal         : $this->_kp;
        $this->_balanceKh = isset($this->_param['Loan']->d->BalanceHealthInsurance)   ? $this->_param['Loan']->d->BalanceHealthInsurance   : $this->_kh;
        $this->_balanceKl = isset($this->_param['Loan']->d->BalanceLoanInsurance)     ? $this->_param['Loan']->d->BalanceLoanInsurance     : $this->_kl;
        $this->_balanceKa = isset($this->_param['Loan']->d->BalanceAdministrativeFee) ? $this->_param['Loan']->d->BalanceAdministrativeFee : $this->_ka;
        
        //$this->_annualEffectiveRate;
                
        $this->_xpPmt     = isset($this->_param['Loan']->d->PaymentMnt)          ? $this->_param['Loan']->d->PaymentMnt          : $this->_pmt;
        $this->_xpPmtDate = isset($this->_param['Loan']->d->NextPaymentDate)     ? $this->_param['Loan']->d->NextPaymentDate     : $this->_param['FirstPaymentDate'];
        $this->_rFromDate = isset($this->_param['Loan']->d->PreviousPaymentDate) ? $this->_param['Loan']->d->PreviousPaymentDate : $this->_param['PrincipalGraceEndDate'];

        $this->_schedule = $this->_getSchedule();
    }
    
    /*
     * Load actual payments in the regular cycle
     */
    protected function _loadActualPmts()
    {
        $c = 0;
        if($this->_param['Loan']) {
            foreach($this->_param['Loan']->r->payments->toArray() as $p) {    
                $this->_schPmt[$c]['inx']               = $c + 1;
                $this->_schPmt[$c]['Date']              = $p['Date'];
                $this->_schPmt[$c]['HealthInsurance']   = $p['HealthInsurance'];
                $this->_schPmt[$c]['LoanInsurance']     = $p['LoanInsurance'];
                $this->_schPmt[$c]['AdministrativeFee'] = $p['AdministrativeFee'];
                $this->_schPmt[$c]['Principal']         = $p['Principal'];
                $this->_schPmt[$c]['Interest']          = $p['Interest']; //
                $this->_schPmt[$c]['Pmt']               = $p['Pmt'];
                $this->_schPmt[$c]['Penalties']         = $p['Penalties'];
                $this->_schPmt[$c]['Delay']             = $p['Delay'];
                $this->_schPmt[$c]['BalancePrincipal']  = $p['BalancePrincipal'];
                $this->_schPmt[$c]['ReceiptId']         = $p['ReceiptId'];
                $this->_schPmt[$c]['TransactionId']     = $p['TransactionId'];
                $this->_schPmt[$c]['Username']          = $p['Username'];
                $this->_schPmt[$c]['Paid']              = true;
            
                $this->_schTotal['HealthInsurance']   += $p['HealthInsurance'];
                $this->_schTotal['LoanInsurance']     += $p['LoanInsurance'];
                $this->_schTotal['AdministrativeFee'] += $p['AdministrativeFee'];
                $this->_schTotal['Principal']         += $p['Principal'];
                $this->_schTotal['Interest']          += $p['Interest'];
                $this->_schTotal['Pmt']               += $p['Pmt'];
                $this->_schTotal['Penalties']         += $p['Penalties'];
                $this->_schTotal['Delay']             += $p['Delay'];
                
                $c++;
            } 
            $this->_actualPaymentsNumber = $c;
        }
    }

    /*
     * Load scheduled payments
     */
    protected function _loadSchPmts()
    {
        $c = count($this->_schPmt);
        $balanceKp = $c ? $this->_schPmt[$c-1]['BalancePrincipal'] : $this->_balanceKp;
        
        $k = $c;
        while($balanceKp > 0) {
 	    $data[$k] = $this->_nextPaymentData();
    
            $this->_balanceKp = $data[$k]['balanceKp'];
            $this->_balanceKh = $data[$k]['balanceKh'];
            $this->_balanceKl = $data[$k]['balanceKl'];
            $this->_balanceKa = $data[$k]['balanceKa'];
            
            $this->_xpPmt     = $data[$k]['xpPmt'];
            $this->_xpPmtDate = $data[$k]['xpPmtDate']; //
            $this->_rFromDate = $data[$k]['rFromDate']; //
    
            $this->_schPmt[$k]['inx']               = $k + 1;
            $this->_schPmt[$k]['Date']              = $data[$k]['rFromDate'];
            $this->_schPmt[$k]['HealthInsurance']   = $data[$k]['hFee'];
            $this->_schPmt[$k]['LoanInsurance']     = $data[$k]['lFee'];
            $this->_schPmt[$k]['AdministrativeFee'] = $data[$k]['aFee'];
            $this->_schPmt[$k]['Principal']         = $data[$k]['principal'];
            $this->_schPmt[$k]['Interest']          = $data[$k]['interest']; //
            $this->_schPmt[$k]['Pmt']               = $data[$k]['pmt'];
            $this->_schPmt[$k]['Penalties']         = $data[$k]['penalties'];
            $this->_schPmt[$k]['Delay']             = $data[$k]['delay'];
            $this->_schPmt[$k]['BalancePrincipal']  = $data[$k]['balanceKp'];
            $this->_schPmt[$k]['BalanceHealthInsurance']  = $data[$k]['balanceKh'];
            $this->_schPmt[$k]['BalanceLoanInsurance']  = $data[$k]['balanceKl'];
            $this->_schPmt[$k]['BalanceAdministrativeFee']  = $data[$k]['balanceKa'];
            $this->_schPmt[$k]['ReceiptId']         = '-';
            $this->_schPmt[$k]['TransactionId']     = '-';
            $this->_schPmt[$k]['Username']          = '-';
            $this->_schPmt[$k]['Paid']              = false;
    
            $this->_schTotal['HealthInsurance']   += $data[$k]['hFee'];
            $this->_schTotal['LoanInsurance']     += $data[$k]['lFee'];
            $this->_schTotal['AdministrativeFee'] += $data[$k]['aFee'];
            $this->_schTotal['Principal']         += $data[$k]['principal'];
            $this->_schTotal['Interest']          += $data[$k]['interest'];
            $this->_schTotal['Pmt']               += $data[$k]['pmt'];
            $this->_schTotal['Penalties']         += $data[$k]['penalties'];
            $this->_schTotal['Delay']             += $data[$k]['delay'];
       
            $balanceKp                    =  $data[$k]['balanceKp'];
            $k++;  
        }
        
        
        if ($k > $c) {
            $this->_schPmt[$k-1]['Principal']        += $data[$k-1]['balanceKp'];
            $this->_schPmt[$k-1]['Pmt']              += $data[$k-1]['balanceKp'];
            $this->_schPmt[$k-1]['BalancePrincipal'] = 0;
            $this->_schPmt[$k-1]['BalanceHealthInsurance'] = 0;
            $this->_schPmt[$k-1]['BalanceLoanInsurance'] = 0;
            $this->_schPmt[$k-1]['BalanceAdministrativeFee'] = 0;
            $this->_schTotal['Principal']           += $data[$k-1]['balanceKp'];
            $this->_schTotal['Pmt']                 += $data[$k-1]['balanceKp'];
            $data[$k]['balanceKp']          = 0;
        
            if($c == 0) {
                $this->_schPmt[0]['Pmt']      += $this->_gracePmt;
                $this->_schPmt[0]['Interest'] += $this->_gracePmt;
                $this->_schTotal['Pmt']       += $this->_gracePmt;
                $this->_schTotal['Interest']  += $this->_gracePmt;
            }
        }
    }
    
    protected function _isSchPmtValid()
    {
        $status = isset($this->_param['Loan']->d->Status) ? $this->_param['Loan']->d->Status : '';
        switch ($status) {
            case 'New': return true;
            case 'Submitted':  return true;
            case 'Re-Submitted':  return true;
            case 'Reviewed':  return true;
            case 'Approved':  return true;
            case 'Executed':  return true;
            case 'Disbursed':  return true;
            case 'Paid':  return false;
            case 'Withdrawn':  return false;
            case 'Rejected':  return true;
            case 'Written-Off': return false;
            case 'Recovered': return false;
        }
        return true;
    }
    
    protected function _getSchedule()
    {
        $this->_schPmt = array();
        $this->_schTotal = array(
            'inx' => Zend_Registry::get('Zend_Translate')->_("TOTALs"), 
            'Date' => ' ', 
            'HealthInsurance' => 0, 
            'LoanInsurance' => 0, 
            'AdministrativeFee' => 0, 
            'Principal' => 0, 
            'Interest' => 0, 
            'Pmt' => ' ', 
            'Penalties' => 0,
            'Delay' => 0,
            'BalancePrincipal' => ' ',
            'ReceiptId' => ' ',
            'TransactionId' => ' ',
            'Username' => ' '); 
        
        $this->_loadActualPmts();
        if($this->_isSchPmtValid()) $this->_loadSchPmts();
        
        return array('meta' => $this->_metadata, 'data' => $this->_schPmt, 'total' => $this->_schTotal);
    }
        
    protected function _nextPaymentData()
    {
        $data = array();        
        $balance = $this->_balanceKp + $this->_balanceKh + $this->_balanceKl + $this->_balanceKa;
        $rFromDate = new Zend_Date($this->_rFromDate,'Y-m-d');
        $pmtDate = new Zend_Date($this->_xpPmtDate,'Y-m-d');
        $n = ceil($pmtDate->sub($rFromDate)->toValue()/60/60/24); 

        $data['xpPmtDate'] = self::nextPaymentDate($this->_xpPmtDate, $this->_param['PaymentFrequency']);
        $data['rFromDate'] = $this->_xpPmtDate;
        
        $data['interest']  = round(($balance*0.01*($this->_param['AnnualPercentageRate']/$this->_param['CalendarType'])*$n),2);
        $data['hFee']      = round(($this->_khP * ($this->_pmt - $data['interest'])),2);
        $data['lFee']      = round(($this->_klP * ($this->_pmt - $data['interest'])),2);
        $data['aFee']      = round(($this->_kaP * ($this->_pmt - $data['interest'])),2);
        $data['principal'] = $this->_pmt - $data['interest'] - $data['hFee'] - $data['lFee'] - $data['aFee'];
        
        $data['balanceKp'] = round($this->_balanceKp - $data['principal'],2);
        $data['balanceKh'] = round($this->_balanceKh - $data['hFee'],2);
        $data['balanceKl'] = round($this->_balanceKl - $data['lFee'],2);
        $data['balanceKa'] = round($this->_balanceKa - $data['aFee'],2);
        
        $data['penalties'] = 0;
        $data['delay'] = 0;
        
        if($this->_param['Loan']) {
            $currentDate = new Zend_Date(date('Y-m-d'),'Y-m-d');
            $pmtDate = new Zend_Date($this->_xpPmtDate,'Y-m-d');  
            if($currentDate >= $pmtDate) {
                $data['delay'] = ceil($currentDate->sub($pmtDate)->toValue()/60/60/24);
                if ($data['delay'] > $this->_param['Loan']->d->DelayGrace) {
                    $data['penalties'] = round($data['delay'] * $this->_pmt * ($this->_param['Loan']->d->DelayPenaltyRate/100) + $this->_param['Loan']->d->DelayPenaltyFixedCharge,2);
                }
            } else {
                $data['delay'] = 0;
            }
        }
        
        $data['pmt']       = $this->_pmt + $data['penalties'];
        $data['xpPmt']     = min($data['pmt'], $balance + $data['interest'] + $data['penalties']);
        
        if ($balance < $this->_pmt) {
          $data['hFee']      = $this->_balanceKh;
          $data['lFee']      = $this->_balanceKl;
          $data['aFee']      = $this->_balanceKa;
          $data['principal'] = $this->_balanceKp;
          $data['balanceKp'] = 0;
          $data['balanceKh'] = 0;
          $data['balanceKl'] = 0;
          $data['balanceKa'] = 0;
          $data['pmt']       = $data['interest'] + $data['hFee'] + $data['lFee'] + $data['aFee'] + $data['principal'] + $data['penalties'];
          $data['xpPmt']     = 0;
        }
        return $data;
    }
    
    public function getPmtMntAllocation($mnt)
    {
        $kh = round(($this->_khP * $mnt),2);
        $kl = round(($this->_klP * $mnt),2);
        $ka = round(($this->_kaP * $mnt),2);
        return array(
          'HealthInsurance' => $kh,
          'LoanInsurance' => $kl,
          'AdministrativeFee' => $ka,
          'Principal' => $mnt - $kh - $kl - $ka
        );
    }
    
    public function getParam($param)
    {
        if(isset($this->_param[$param]))
            return $this->_param[$param];
        return;
    }
    
    public function getParams()
    {
        return $this->_param;
    }
    
    public function getSchedule()
    {
        return $this->_schedule;
    }
    
    public function getSchPmtObj()
    {
        $sch = new stdClass();
        foreach($this->_schPmt as $inx1 => $val1) {
            $obj = new stdClass();
            foreach($val1 as $inx2 => $val2) {
                $obj->$inx2 = $val2;
            }
            $sch->$inx1 = $obj;
        }
        return $sch;
    }
    
    private function _loadBalancesToday()
    {
        $this->_balancesToday = array();
        $this->_balancesToday['Total'] = 0;
        $this->_balancesToday['Principal'] = 0;
        $this->_balancesToday['LoanInsurance'] = 0;
        $this->_balancesToday['HealthInsurance'] = 0;
        $this->_balancesToday['AdministrativeFee'] = 0;
        $this->_balancesToday['Interest'] = 0;
        $this->_balancesToday['Penalties'] = 0;
        $rFromDate = $this->_param['Loan']->d->DeliveryDate;
        foreach($this->_schPmt as $pmt) {
            if(!$pmt['Paid']) {
                $this->_balancesToday['Total'] += $pmt['Pmt'];
                $this->_balancesToday['Principal'] += $pmt['Principal'];
                $this->_balancesToday['LoanInsurance'] += $pmt['LoanInsurance'];
                $this->_balancesToday['HealthInsurance'] += $pmt['HealthInsurance'];
                $this->_balancesToday['AdministrativeFee'] += $pmt['AdministrativeFee'];
                $this->_balancesToday['Interest'] += $pmt['Interest'];
                $this->_balancesToday['Penalties'] += $pmt['Penalties'];
                if($rFromDate >= date('Y-m-d')) {
                    $this->_balancesToday['Total'] -= $pmt['Interest'];
                    $this->_balancesToday['Interest'] -= $pmt['Interest'];
                } elseif($pmt['Date'] > date('Y-m-d')) {
                    $fromDate = new Zend_Date($rFromDate,'Y-m-d'); 
                    $pmtDate = new Zend_Date($pmt['Date'],'Y-m-d'); 
                    $currentDate = new Zend_Date(date('Y-m-d'),'Y-m-d');
                    $n = ceil($pmtDate->sub($fromDate)->toValue()/60/60/24);
                    $m = ceil($currentDate->sub($fromDate)->toValue()/60/60/24);
                    $this->_balancesToday['Total'] -= round($pmt['Interest']*(1-($m/$n)),2); 
                    $this->_balancesToday['Interest'] -= round($pmt['Interest']*(1-($m/$n)),2); 
                }
            }
            $d = strtotime($pmt['Date'] . sprintf(' -%s days', $pmt['Delay']));
            $rFromDate = date('Y-m-d', $d);
        }
    }
    
    public function getBalance($param = 'Total')
    {
        /*
        $balance = 0;
        $rFromDate = $this->_param['Loan']->d->DeliveryDate;
        foreach($this->_schPmt as $pmt) {
            if(!$pmt['Paid']) {
                $balance += $pmt['Pmt'];
                if($rFromDate >= date('Y-m-d')) {
                    $balance -= $pmt['Interest'];
                } elseif($pmt['Date'] > date('Y-m-d')) {
                    $fromDate = new Zend_Date($rFromDate,'Y-m-d'); 
                    $pmtDate = new Zend_Date($pmt['Date'],'Y-m-d'); 
                    $currentDate = new Zend_Date(date('Y-m-d'),'Y-m-d');
                    $n = ceil($pmtDate->sub($fromDate)->toValue()/60/60/24);
                    $m = ceil($currentDate->sub($fromDate)->toValue()/60/60/24);
                    $balance -= round($pmt['Interest']*(1-($m/$n)),2); 
                }
            }
            $d = strtotime($pmt['Date'] . sprintf(' -%s days', $pmt['Delay']));
            $rFromDate = date('Y-m-d', $d);
        }
         * 
         */
        return  $this->_balancesToday[$param];
    }
    
    public function getInterestBalance() 
    {
        return $this->getBalance() - 
                $this->_param['Loan']->d->BalanceLoanInsurance -
                $this->_param['Loan']->d->BalanceHealthInsurance -
                $this->_param['Loan']->d->BalanceAdministrativeFee -
                $this->_param['Loan']->d->BalancePrincipal;
    }
    
    public function getBalanceEOL()
    {
        $balance = 0;
        foreach($this->_schPmt as $pmt) {
            if(!$pmt['Paid']) {
                $balance += $pmt['Pmt'];
            }
        }
        return $balance;
    }
    
    public function getNextDuePayment()
    {
        if(isset($this->_schPmt[$this->_actualPaymentsNumber]))
            return $this->_schPmt[$this->_actualPaymentsNumber];
        return null;
    }
    
    public function toArray()
    {
        return $this->_param + array('Schedule' => $this->_schedule);
    }
    
    static function nextPaymentDate($prevDate, $paymentFreq)
    {
        $d = new Zend_Date($prevDate,'Y-m-d');
        switch($paymentFreq) {
            case "W" : $d->addDay(7);   break;
            case "BW": $d->addDay(14);  break;
            case "M" : $d->addMonth(1); break;
            case "Q" : $d->addMonth(3); break;
            case "SA": $d->addMonth(6); break;
            case "A" : $d->addYear(1);  break;
        }
        return $d->get('Y-m-d');
    }
    
    /**
     * Returns the maximum allowed first payment date
     * Invalid params cause exception to be thrown
     * 
     * @param string $deliveryDate YYYY-MM-DD. Throws exception if hollidays.
     * @param integer $principalGrace Positive integer
     * @param string $paymentFrequency W, BW, M, Q, SA, A
     * @return string YYYY-MM-DD
     * @throws Exception
     * @todo Validate that returned date is not hollidays
     */
    static function getMaxFirstPaymentDate($deliveryDate, $principalGrace, $paymentFrequency)
    {
        try {
            Emms_Loan::validateDeliveryDate($deliveryDate);
            Emms_Loan::validatePrincipalGraceMargin($principalGrace);
            Emms_Loan::validatePaymentFrequency($paymentFrequency);
            $d = new Zend_Date($deliveryDate,'Y-m-d');
            return self::nextPaymentDate($d->addDay($principalGrace)->get('Y-m-d'), $paymentFrequency);
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
    
    public function translate($value)
    {            
        return Zend_Registry::get('Zend_Translate')->_($value);
    }
}